////////////////////////////////////////////////////////////////////////////
//  Copyright 2009 - 2011 Zach Snow                                       //
////////////////////////////////////////////////////////////////////////////
//  This file is part of WindowsWhere.                                    //
//                                                                        //
//  WindowsWhere is free software: you can redistribute it and/or modify  //
//  it under the terms of the GNU General Public License as published by  //
//  the Free Software Foundation, either version 3 of the License, or     //
//  (at your option) any later version.                                   //
//                                                                        //
//  WindowsWhere is distributed in the hope that it will be useful,       //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of        //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         //
//  GNU General Public License for more details.                          //
//                                                                        //
//  You should have received a copy of the GNU General Public License     //
//  along with WindowsWhere.  If not, see <http://www.gnu.org/licenses/>. //
////////////////////////////////////////////////////////////////////////////
// This file was derived from GlobalHook.cs, by Chris Wilson              //
////////////////////////////////////////////////////////////////////////////
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsWhere
{
  public class GlobalHooks
  {
    public delegate void HookReplacedEventHandler();
    public delegate void WindowEventHandler(IntPtr Handle);
    public delegate void SysCommandEventHandler(int SysCommand, int lParam);
    public delegate void ActivateShellWindowEventHandler();
    public delegate void TaskmanEventHandler();
    public delegate void BasicHookEventHandler(IntPtr Handle1, IntPtr Handle2);
    public delegate void WndProcEventHandler(IntPtr Handle, IntPtr Message, IntPtr wParam, IntPtr lParam);

    // Functions imported from our unmanaged DLL
    [DllImport("GlobalHook.dll")]
    private static extern bool InitializeCbtHook(int threadID, IntPtr DestWindow);
    [DllImport("GlobalHook.dll")]
    private static extern void UninitializeCbtHook();
    [DllImport("GlobalHook.dll")]
    private static extern bool InitializeShellHook(int threadID, IntPtr DestWindow);
    [DllImport("GlobalHook.dll")]
    private static extern void UninitializeShellHook();
    [DllImport("GlobalHook.dll")]
    private static extern void InitializeKeyboardHook(int threadID, IntPtr DestWindow);
    [DllImport("GlobalHook.dll")]
    private static extern void UninitializeKeyboardHook();
    [DllImport("GlobalHook.dll")]
    private static extern void InitializeMouseHook(int threadID, IntPtr DestWindow);
    [DllImport("GlobalHook.dll")]
    private static extern void UninitializeMouseHook();
    [DllImport("GlobalHook.dll")]
    private static extern void InitializeKeyboardLLHook(int threadID, IntPtr DestWindow);
    [DllImport("GlobalHook.dll")]
    private static extern void UninitializeKeyboardLLHook();
    [DllImport("GlobalHook.dll")]
    private static extern void InitializeMouseLLHook(int threadID, IntPtr DestWindow);
    [DllImport("GlobalHook.dll")]
    private static extern void UninitializeMouseLLHook();
    [DllImport("GlobalHook.dll")]
    private static extern void InitializeCallWndProcHook(int threadID, IntPtr DestWindow);
    [DllImport("GlobalHook.dll")]
    private static extern void UninitializeCallWndProcHook();
    [DllImport("GlobalHook.dll")]
    private static extern void InitializeGetMsgHook(int threadID, IntPtr DestWindow);
    [DllImport("GlobalHook.dll")]
    private static extern void UninitializeGetMsgHook();

    // API call needed to retreive the value of the messages to intercept from the unmanaged DLL
    [DllImport("user32.dll")]
    private static extern int RegisterWindowMessage(string lpString);
    [DllImport("user32.dll")]
    private static extern IntPtr GetProp(IntPtr hWnd, string lpString);
    [DllImport("user32.dll")]
    private static extern IntPtr GetDesktopWindow();

    // Handle of the window intercepting messages
    private IntPtr _Handle;

    private CBTHook _CBT;
    private ShellHook _Shell;
    private KeyboardHook _Keyboard;
    private MouseHook _Mouse;
    private KeyboardLLHook _KeyboardLL;
    private MouseLLHook _MouseLL;
    private CallWndProcHook _CallWndProc;
    private GetMsgHook _GetMsg;

    private ILog _Log;

    public GlobalHooks(IntPtr Handle)
      : this(Handle, new NullLog())
    { }

    public GlobalHooks(IntPtr Handle, ILog log)
    {
      _Log = log;
      _Handle = Handle;

      _CBT = new CBTHook(_Handle, _Log);
      _Shell = new ShellHook(_Handle, _Log);
      _Keyboard = new KeyboardHook(_Handle, _Log);
      _Mouse = new MouseHook(_Handle, _Log);
      _KeyboardLL = new KeyboardLLHook(_Handle, _Log);
      _MouseLL = new MouseLLHook(_Handle, _Log);
      _CallWndProc = new CallWndProcHook(_Handle, _Log);
      _GetMsg = new GetMsgHook(_Handle, _Log);
    }

    ~GlobalHooks()
    {
      StopHook(_CBT);
      StopHook(_Shell);
      StopHook(_Keyboard);
      StopHook(_Mouse);
      StopHook(_KeyboardLL);
      StopHook(_MouseLL);
      StopHook(_CallWndProc);
      StopHook(_GetMsg);
    }

    private void StopHook(Hook h)
    {
      if (h != null)
        h.Stop();
    }

    public void ProcessWindowMessage(ref System.Windows.Forms.Message m)
    {
      ProcessHook(_CBT, ref m);
      ProcessHook(_Shell, ref m);
      ProcessHook(_Keyboard, ref m);
      ProcessHook(_Mouse, ref m);
      ProcessHook(_KeyboardLL, ref m);
      ProcessHook(_MouseLL, ref m);
      ProcessHook(_CallWndProc, ref m);
      ProcessHook(_GetMsg, ref m);
    }

    private void ProcessHook(Hook h, ref System.Windows.Forms.Message m)
    {
      if (h != null)
        h.ProcessWindowMessage(ref m);
    }

    #region Accessors

    public CBTHook CBT
    {
      get { return _CBT; }
    }

    public ShellHook Shell
    {
      get { return _Shell; }
    }

    public KeyboardHook Keyboard
    {
      get { return _Keyboard; }
    }

    public MouseHook Mouse
    {
      get { return _Mouse; }
    }

    public KeyboardLLHook KeyboardLL
    {
      get { return _KeyboardLL; }
    }

    public MouseLLHook MouseLL
    {
      get { return _MouseLL; }
    }

    public CallWndProcHook CallWndProc
    {
      get { return _CallWndProc; }
    }

    public GetMsgHook GetMsg
    {
      get { return _GetMsg; }
    }

    #endregion

    public abstract class Hook
    {
      protected bool _IsActive = false;
      protected IntPtr _Handle;

      protected ILog _Log;

      public Hook(IntPtr Handle, ILog log)
      {
        _Log = log;
        _Handle = Handle;
      }

      public void Start()
      {
        if (!_IsActive)
        {
          _IsActive = true;
          OnStart();
        }
      }

      public void Stop()
      {
        if (_IsActive)
        {
          OnStop();
          _IsActive = false;
        }
      }

      ~Hook()
      {
        Stop();
      }

      public bool IsActive
      {
        get { return _IsActive; }
      }

      protected abstract void OnStart();
      protected abstract void OnStop();
      public abstract void ProcessWindowMessage(ref System.Windows.Forms.Message m);
    }

    public class CBTHook : Hook
    {
      // Values retreived with RegisterWindowMessage
      private int _MsgID_CBT_HookReplaced;
      private int _MsgID_CBT_Activate;
      private int _MsgID_CBT_CreateWnd;
      private int _MsgID_CBT_DestroyWnd;
      private int _MsgID_CBT_MinMax;
      private int _MsgID_CBT_MoveSize;
      private int _MsgID_CBT_SetFocus;
      private int _MsgID_CBT_SysCommand;

      public event HookReplacedEventHandler HookReplaced;
      public event WindowEventHandler Activate;
      public event WindowEventHandler CreateWindow;
      public event WindowEventHandler DestroyWindow;
      public event WindowEventHandler MinMax;
      public event WindowEventHandler MoveSize;
      public event WindowEventHandler SetFocus;
      public event SysCommandEventHandler SysCommand;

      public CBTHook(IntPtr Handle, ILog log)
        : base(Handle, log)
      {
      }

      protected override void OnStart()
      {
        // Retreive the message IDs that we'll look for in WndProc
        _MsgID_CBT_HookReplaced = RegisterWindowMessage("WW_HOOK_CBT_REPLACED");
        _MsgID_CBT_Activate = RegisterWindowMessage("WW_HOOK_HCBT_ACTIVATE");
        _MsgID_CBT_CreateWnd = RegisterWindowMessage("WW_HOOK_HCBT_CREATEWND");
        _MsgID_CBT_DestroyWnd = RegisterWindowMessage("WW_HOOK_HCBT_DESTROYWND");
        _MsgID_CBT_MinMax = RegisterWindowMessage("WW_HOOK_HCBT_MINMAX");
        _MsgID_CBT_MoveSize = RegisterWindowMessage("WW_HOOK_HCBT_MOVESIZE");
        _MsgID_CBT_SetFocus = RegisterWindowMessage("WW_HOOK_HCBT_SETFOCUS");
        _MsgID_CBT_SysCommand = RegisterWindowMessage("WW_HOOK_HCBT_SYSCOMMAND");

        // Start the hook
        InitializeCbtHook(0, _Handle);
      }

      protected override void OnStop()
      {
        UninitializeCbtHook();
      }

      public override void ProcessWindowMessage(ref System.Windows.Forms.Message m)
      {
        if (m.Msg == _MsgID_CBT_HookReplaced)
        {
          if (HookReplaced != null)
            HookReplaced();
        }
        else if (m.Msg == _MsgID_CBT_Activate)
        {
          if (Activate != null)
            Activate(m.WParam);
        }
        else if (m.Msg == _MsgID_CBT_CreateWnd)
        {
          if (CreateWindow != null)
          {
            CreateWindow(m.WParam);
          }
          _Log.Log("create window CBT hook invoked");
        }
        else if (m.Msg == _MsgID_CBT_DestroyWnd)
        {
          if (DestroyWindow != null)
            DestroyWindow(m.WParam);
        }
        else if (m.Msg == _MsgID_CBT_MinMax)
        {
          if (MinMax != null)
            MinMax(m.WParam);
        }
        else if (m.Msg == _MsgID_CBT_MoveSize)
        {
          if (MoveSize != null)
            MoveSize(m.WParam);
        }
        else if (m.Msg == _MsgID_CBT_SetFocus)
        {
          if (SetFocus != null)
            SetFocus(m.WParam);
        }
        else if (m.Msg == _MsgID_CBT_SysCommand)
        {
          if (SysCommand != null)
            SysCommand(m.WParam.ToInt32(), m.LParam.ToInt32());
        }
      }
    }

    public class ShellHook : Hook
    {
      // Values retreived with RegisterWindowMessage
      private int _MsgID_Shell_ActivateShellWindow;
      private int _MsgID_Shell_GetMinRect;
      private int _MsgID_Shell_Language;
      private int _MsgID_Shell_Redraw;
      private int _MsgID_Shell_Taskman;
      private int _MsgID_Shell_HookReplaced;
      private int _MsgID_Shell_WindowActivated;
      private int _MsgID_Shell_WindowCreated;
      private int _MsgID_Shell_WindowDestroyed;

      public event HookReplacedEventHandler HookReplaced;
      public event ActivateShellWindowEventHandler ActivateShellWindow;
      public event WindowEventHandler GetMinRect;
      public event WindowEventHandler Language;
      public event WindowEventHandler Redraw;
      public event TaskmanEventHandler Taskman;
      public event WindowEventHandler WindowActivated;
      public event WindowEventHandler WindowCreated;
      public event WindowEventHandler WindowDestroyed;

      public ShellHook(IntPtr Handle, ILog log)
        : base(Handle, log)
      {
      }

      protected override void OnStart()
      {
        // Retreive the message IDs that we'll look for in WndProc
        _MsgID_Shell_HookReplaced = RegisterWindowMessage("WW_HOOK_SHELL_REPLACED");
        _MsgID_Shell_ActivateShellWindow = RegisterWindowMessage("WW_HOOK_HSHELL_ACTIVATESHELLWINDOW");
        _MsgID_Shell_GetMinRect = RegisterWindowMessage("WW_HOOK_HSHELL_GETMINRECT");
        _MsgID_Shell_Language = RegisterWindowMessage("WW_HOOK_HSHELL_LANGUAGE");
        _MsgID_Shell_Redraw = RegisterWindowMessage("WW_HOOK_HSHELL_REDRAW");
        _MsgID_Shell_Taskman = RegisterWindowMessage("WW_HOOK_HSHELL_TASKMAN");
        _MsgID_Shell_WindowActivated = RegisterWindowMessage("WW_HOOK_HSHELL_WINDOWACTIVATED");
        _MsgID_Shell_WindowCreated = RegisterWindowMessage("WW_HOOK_HSHELL_WINDOWCREATED");
        _MsgID_Shell_WindowDestroyed = RegisterWindowMessage("WW_HOOK_HSHELL_WINDOWDESTROYED");

        // Start the hook
        InitializeShellHook(0, _Handle);
      }

      protected override void OnStop()
      {
        UninitializeShellHook();
      }

      public override void ProcessWindowMessage(ref System.Windows.Forms.Message m)
      {
        if (m.Msg == _MsgID_Shell_HookReplaced)
        {
          if (HookReplaced != null)
            HookReplaced();
        }
        else if (m.Msg == _MsgID_Shell_ActivateShellWindow)
        {
          if (ActivateShellWindow != null)
            ActivateShellWindow();
        }
        else if (m.Msg == _MsgID_Shell_GetMinRect)
        {
          if (GetMinRect != null)
            GetMinRect(m.WParam);
        }
        else if (m.Msg == _MsgID_Shell_Language)
        {
          if (Language != null)
            Language(m.WParam);
        }
        else if (m.Msg == _MsgID_Shell_Redraw)
        {
          if (Redraw != null)
            Redraw(m.WParam);
        }
        else if (m.Msg == _MsgID_Shell_Taskman)
        {
          if (Taskman != null)
            Taskman();
        }
        else if (m.Msg == _MsgID_Shell_WindowActivated)
        {
          if (WindowActivated != null)
            WindowActivated(m.WParam);
        }
        else if (m.Msg == _MsgID_Shell_WindowCreated)
        {
          if (WindowCreated != null)
          {
            WindowCreated(m.WParam);
          }
          _Log.Log("create window shell hook invoked");
        }
        else if (m.Msg == _MsgID_Shell_WindowDestroyed)
        {
          if (WindowDestroyed != null)
            WindowDestroyed(m.WParam);
        }
      }
    }

    public class KeyboardHook : Hook
    {
      // Values retreived with RegisterWindowMessage
      private int _MsgID_Keyboard;
      private int _MsgID_Keyboard_HookReplaced;

      public event HookReplacedEventHandler HookReplaced;
      public event BasicHookEventHandler KeyboardEvent;

      public KeyboardHook(IntPtr Handle, ILog log)
        : base(Handle, log)
      {
      }

      protected override void OnStart()
      {
        // Retreive the message IDs that we'll look for in WndProc
        _MsgID_Keyboard = RegisterWindowMessage("WW_HOOK_KEYBOARD");
        _MsgID_Keyboard_HookReplaced = RegisterWindowMessage("WW_HOOK_KEYBOARD_REPLACED");

        // Start the hook
        InitializeKeyboardHook(0, _Handle);
      }
      protected override void OnStop()
      {
        UninitializeKeyboardHook();
      }

      public override void ProcessWindowMessage(ref System.Windows.Forms.Message m)
      {
        if (m.Msg == _MsgID_Keyboard)
        {
          if (KeyboardEvent != null)
            KeyboardEvent(m.WParam, m.LParam);
        }
        else if (m.Msg == _MsgID_Keyboard_HookReplaced)
        {
          if (HookReplaced != null)
            HookReplaced();
        }
      }
    }

    public class MouseHook : Hook
    {
      // Values retreived with RegisterWindowMessage
      private int _MsgID_Mouse;
      private int _MsgID_Mouse_HookReplaced;

      public event HookReplacedEventHandler HookReplaced;
      public event BasicHookEventHandler MouseEvent;

      public MouseHook(IntPtr Handle, ILog log)
        : base(Handle, log)
      {
      }

      protected override void OnStart()
      {
        // Retreive the message IDs that we'll look for in WndProc
        _MsgID_Mouse = RegisterWindowMessage("WW_HOOK_MOUSE");
        _MsgID_Mouse_HookReplaced = RegisterWindowMessage("WW_HOOK_MOUSE_REPLACED");

        // Start the hook
        InitializeMouseHook(0, _Handle);
      }
      protected override void OnStop()
      {
        UninitializeMouseHook();
      }

      public override void ProcessWindowMessage(ref System.Windows.Forms.Message m)
      {
        if (m.Msg == _MsgID_Mouse)
        {
          if (MouseEvent != null)
            MouseEvent(m.WParam, m.LParam);
        }
        else if (m.Msg == _MsgID_Mouse_HookReplaced)
        {
          if (HookReplaced != null)
            HookReplaced();
        }
      }
    }

    public class KeyboardLLHook : Hook
    {
      // Values retreived with RegisterWindowMessage
      private int _MsgID_KeyboardLL;
      private int _MsgID_KeyboardLL_HookReplaced;

      public event HookReplacedEventHandler HookReplaced;
      public event BasicHookEventHandler KeyboardLLEvent;

      public KeyboardLLHook(IntPtr Handle, ILog log)
        : base(Handle, log)
      {
      }

      protected override void OnStart()
      {
        // Retreive the message IDs that we'll look for in WndProc
        _MsgID_KeyboardLL = RegisterWindowMessage("WW_HOOK_KEYBOARDLL");
        _MsgID_KeyboardLL_HookReplaced = RegisterWindowMessage("WW_HOOK_KEYBOARDLL_REPLACED");

        // Start the hook
        InitializeKeyboardLLHook(0, _Handle);
      }

      protected override void OnStop()
      {
        UninitializeKeyboardLLHook();
      }

      public override void ProcessWindowMessage(ref System.Windows.Forms.Message m)
      {
        if (m.Msg == _MsgID_KeyboardLL)
        {
          if (KeyboardLLEvent != null)
            KeyboardLLEvent(m.WParam, m.LParam);
        }
        else if (m.Msg == _MsgID_KeyboardLL_HookReplaced)
        {
          if (HookReplaced != null)
            HookReplaced();
        }
      }
    }

    public class MouseLLHook : Hook
    {
      // Values retreived with RegisterWindowMessage
      private int _MsgID_MouseLL;
      private int _MsgID_MouseLL_HookReplaced;

      public event HookReplacedEventHandler HookReplaced;
      public event BasicHookEventHandler MouseLLEvent;
      public event MouseEventHandler MouseDown;
      public event MouseEventHandler MouseMove;
      public event MouseEventHandler MouseUp;

      private const int WM_MOUSEMOVE = 0x0200;
      private const int WM_LBUTTONDOWN = 0x0201;
      private const int WM_LBUTTONUP = 0x0202;
      private const int WM_LBUTTONDBLCLK = 0x0203;
      private const int WM_RBUTTONDOWN = 0x0204;
      private const int WM_RBUTTONUP = 0x0205;
      private const int WM_RBUTTONDBLCLK = 0x0206;
      private const int WM_MBUTTONDOWN = 0x0207;
      private const int WM_MBUTTONUP = 0x0208;
      private const int WM_MBUTTONDBLCLK = 0x0209;
      private const int WM_MOUSEWHEEL = 0x020A;

      struct MSLLHOOKSTRUCT
      {
        public System.Drawing.Point pt;
        public int mouseData;
        public int flags;
        public int time;
        public IntPtr dwExtraInfo;
      };

      public MouseLLHook(IntPtr Handle, ILog log)
        : base(Handle, log)
      {
      }

      protected override void OnStart()
      {
        // Retreive the message IDs that we'll look for in WndProc
        _MsgID_MouseLL = RegisterWindowMessage("WW_HOOK_MOUSELL");
        _MsgID_MouseLL_HookReplaced = RegisterWindowMessage("WW_HOOK_MOUSELL_REPLACED");

        // Start the hook
        InitializeMouseLLHook(0, _Handle);
      }

      protected override void OnStop()
      {
        UninitializeMouseLLHook();
      }

      public override void ProcessWindowMessage(ref System.Windows.Forms.Message m)
      {
        if (m.Msg == _MsgID_MouseLL)
        {
          if (MouseLLEvent != null)
            MouseLLEvent(m.WParam, m.LParam);

          MSLLHOOKSTRUCT M = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(m.LParam, typeof(MSLLHOOKSTRUCT));

          if (m.WParam.ToInt32() == WM_MOUSEMOVE)
          {
            if (MouseMove != null)
              MouseMove(this, new MouseEventArgs(MouseButtons.None, 0, M.pt.X, M.pt.Y, 0));
          }
          else if (m.WParam.ToInt32() == WM_LBUTTONDOWN)
          {
            if (MouseDown != null)
              MouseDown(this, new MouseEventArgs(MouseButtons.Left, 0, M.pt.X, M.pt.Y, 0));
          }
          else if (m.WParam.ToInt32() == WM_RBUTTONDOWN)
          {
            if (MouseDown != null)
              MouseDown(this, new MouseEventArgs(MouseButtons.Right, 0, M.pt.X, M.pt.Y, 0));
          }
          else if (m.WParam.ToInt32() == WM_LBUTTONUP)
          {
            if (MouseUp != null)
              MouseUp(this, new MouseEventArgs(MouseButtons.Left, 0, M.pt.X, M.pt.Y, 0));
          }
          else if (m.WParam.ToInt32() == WM_RBUTTONUP)
          {
            if (MouseUp != null)
              MouseUp(this, new MouseEventArgs(MouseButtons.Right, 0, M.pt.X, M.pt.Y, 0));
          }
        }
        else if (m.Msg == _MsgID_MouseLL_HookReplaced)
        {
          if (HookReplaced != null)
            HookReplaced();
        }
      }
    }
    public class CallWndProcHook : Hook
    {
      // Values retreived with RegisterWindowMessage
      private int _MsgID_CallWndProc;
      private int _MsgID_CallWndProc_Params;
      private int _MsgID_CallWndProc_HookReplaced;

      public event HookReplacedEventHandler HookReplaced;
      public event WndProcEventHandler CallWndProc;

      private IntPtr _CacheHandle;
      private IntPtr _CacheMessage;

      public CallWndProcHook(IntPtr Handle, ILog log)
        : base(Handle, log)
      {
      }

      protected override void OnStart()
      {
        // Retreive the message IDs that we'll look for in WndProc
        _MsgID_CallWndProc_HookReplaced = RegisterWindowMessage("WW_HOOK_CALLWNDPROC_REPLACED");
        _MsgID_CallWndProc = RegisterWindowMessage("WW_HOOK_CALLWNDPROC");
        _MsgID_CallWndProc_Params = RegisterWindowMessage("WW_HOOK_CALLWNDPROC_PARAMS");

        // Start the hook
        InitializeCallWndProcHook(0, _Handle);
      }

      protected override void OnStop()
      {
        UninitializeCallWndProcHook();
      }

      public override void ProcessWindowMessage(ref System.Windows.Forms.Message m)
      {
        if (m.Msg == _MsgID_CallWndProc)
        {
          _CacheHandle = m.WParam;
          _CacheMessage = m.LParam;
        }
        else if (m.Msg == _MsgID_CallWndProc_Params)
        {
          if (CallWndProc != null && _CacheHandle != IntPtr.Zero && _CacheMessage != IntPtr.Zero)
            CallWndProc(_CacheHandle, _CacheMessage, m.WParam, m.LParam);
          _CacheHandle = IntPtr.Zero;
          _CacheMessage = IntPtr.Zero;
        }
        else if (m.Msg == _MsgID_CallWndProc_HookReplaced)
        {
          if (HookReplaced != null)
            HookReplaced();
        }
      }
    }
    public class GetMsgHook : Hook
    {
      // Values retreived with RegisterWindowMessage
      private int _MsgID_GetMsg;
      private int _MsgID_GetMsg_Params;
      private int _MsgID_GetMsg_HookReplaced;

      public event HookReplacedEventHandler HookReplaced;
      public event WndProcEventHandler GetMsg;

      private IntPtr _CacheHandle;
      private IntPtr _CacheMessage;

      public GetMsgHook(IntPtr Handle, ILog log)
        : base(Handle, log)
      {
      }

      protected override void OnStart()
      {
        // Retreive the message IDs that we'll look for in WndProc
        _MsgID_GetMsg_HookReplaced = RegisterWindowMessage("WW_HOOK_GETMSG_REPLACED");
        _MsgID_GetMsg = RegisterWindowMessage("WW_HOOK_GETMSG");
        _MsgID_GetMsg_Params = RegisterWindowMessage("WW_HOOK_GETMSG_PARAMS");

        // Start the hook
        InitializeGetMsgHook(0, _Handle);
      }

      protected override void OnStop()
      {
        UninitializeGetMsgHook();
      }

      public override void ProcessWindowMessage(ref System.Windows.Forms.Message m)
      {
        if (m.Msg == _MsgID_GetMsg)
        {
          _CacheHandle = m.WParam;
          _CacheMessage = m.LParam;
        }
        else if (m.Msg == _MsgID_GetMsg_Params)
        {
          if (GetMsg != null && _CacheHandle != IntPtr.Zero && _CacheMessage != IntPtr.Zero)
            GetMsg(_CacheHandle, _CacheMessage, m.WParam, m.LParam);
          _CacheHandle = IntPtr.Zero;
          _CacheMessage = IntPtr.Zero;
        }
        else if (m.Msg == _MsgID_GetMsg_HookReplaced)
        {
          if (HookReplaced != null)
            HookReplaced();
        }
      }
    }
  }
}
